/*
 * $Id: IPSweepTask.java,v 1.55 2010/06/16 13:28:06 navdeep_mahajan Exp $
 * 
 * Copyright (C) 2008 General Electric Company. All Rights Reserved.
 * 
 * This software is the confidential and proprietary information of the General
 * Electric Company (GE). You shall not disclose this software and shall use it
 * only in accordance with the terms of the license agreement you entered into
 * with GE.
 * 
 * GE MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
 * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
 * NON-INFRINGEMENT. GE SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 * AS A RESULT OF USING, MODIFYING, OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES.
 */
package com.ge.healthcare.autosc.processor.dispatcher.tasks;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.regexp.RE;
import org.apache.regexp.RESyntaxException;

import com.ge.healthcare.autosc.common.ASCLogger;
import com.ge.healthcare.autosc.common.ASCUtil;
import com.ge.healthcare.autosc.common.commands.FTPCommand;
import com.ge.healthcare.autosc.common.database.dao.IModelTypePropDAO;
import com.ge.healthcare.autosc.common.database.dao.IRequestDAO;
import com.ge.healthcare.autosc.common.database.dao.IRequestDefn2SweepPropertiesDAO;
import com.ge.healthcare.autosc.common.database.dao.IRequestListUserDAO;
import com.ge.healthcare.autosc.common.database.dao.IRequestLoginDetailsDAO;
import com.ge.healthcare.autosc.common.database.dao.IRequestPropertiesDAO;
import com.ge.healthcare.autosc.common.database.dao.IRequestTypeDAO;
import com.ge.healthcare.autosc.common.database.dao.ISweepFileInfoDAO;
import com.ge.healthcare.autosc.common.database.dao.ISweepPropertiesDAO;
import com.ge.healthcare.autosc.common.database.pojo.ActRequest;
import com.ge.healthcare.autosc.common.database.pojo.ActRequestListUser;
import com.ge.healthcare.autosc.common.database.pojo.ActTask;
import com.ge.healthcare.autosc.common.database.pojo.CfgSweepFileInfo;
import com.ge.healthcare.autosc.common.dbLogger.StepLogger;
import com.ge.healthcare.autosc.common.exceptions.FTPCommandException;
import com.ge.healthcare.autosc.common.exceptions.InvalidLoginCredentialsException;
import com.ge.healthcare.autosc.common.util.ASCFileHandler;
import com.ge.healthcare.autosc.common.util.ApplicationInfo;
import com.ge.healthcare.autosc.common.util.ConnectivityUtil;
import com.ge.healthcare.autosc.common.util.GSCCUtil;
import com.ge.healthcare.autosc.common.util.PropertyConstants;
import com.ge.healthcare.autosc.processor.exception.ClientDrvCommandException;
import com.ge.healthcare.autosc.processor.exception.IPSweepException;
import com.ge.healthcare.autosc.processor.iip.comm.ClientDrvCommand;
import com.ge.healthcare.autosc.processor.iip.comm.ClientDrvHeartBeat;
import com.ge.healthcare.autosc.processor.iip.comm.IIPResponseParser;
import com.ge.healthcare.autosc.processor.iip.comm.vo.FileInfo;
import com.ge.med.service.mc.exceptions.MakeConnectionException;

/**
 * Interface for all sweep operations
 * 
 * @author 212042764
 * 
 */
public class IPSweepTask extends IPLegacyTask implements ISweep {

	protected static final String IPSWEEP_PREFIX = "IPSWEEP-";
	// Left variable name as RWX_RW_RW_PERMISSIONS because this can't change
	// unless we allow permissions to be configured per model type basis
	private static final String RWX_RW_RW_PERMISSIONS = "755";
	private static final String ROOT = "root";
	// Static Objects will be populated by spring on loading of application
	// context
	private static IRequestLoginDetailsDAO loginDetailsDAO = null;
	private static IRequestDAO requestDAO = null;
	private static IRequestDefn2SweepPropertiesDAO requestDefn2SweepPropertiesDAO = null;
	private static ISweepFileInfoDAO sweepFileInfoDAO = null;
	private static IRequestListUserDAO requestUserDAO = null;
	private static IRequestTypeDAO requestTypeDAO = null;
	private static IModelTypePropDAO modelTypePropDAO = null;

	// Class variables to be kept aloof from spring unless spring object is
	// retrieved from application context
	protected ActTask task = null;
	protected ActRequest request = null;
	protected Map<String, String> sweepProperties = null;
	protected List<CfgSweepFileInfo> putSweepFiles = null;
	protected List<CfgSweepFileInfo> getSweepFiles = null;
	protected Object[] scriptOutFiles = null;
	protected String sweepDir = null;
	protected Map<String, String> requestProperties = null;
	protected List<String> retrievedFiles = new ArrayList<String>();

	protected String acceptedUserName = null;
	protected String acceptedPassword = null;
	protected File scriptOutFile = null;
	protected File scriptErrFile = null;
	protected String jobDir = null;

	protected int timeout = 120000;

	/**
	 * Main method invoked from Processor MDP
	 * 
	 * @see com.ge.healthcare.autosc.processor.dispatcher.tasks.BasicTask#executeTask(com.ge.healthcare.autosc.common.database.pojo.ActTask)
	 */
	public void executeTask(ActTask task) {
		String methodName = "executeTask";
		boolean success = false;
		ASCLogger.debug(this.getClass(), methodName,
				"Starting IPSweepTask for task:" + task);
		this.task = task;
		try {
			this.init();
			this.validateMinimumInput();
			try {
				this.makeConnection();
				this.performLogin();
				this.runClientDrv();
				this.downloadSetup();
				this.executeScript();
				this.fetchResults();
				this.cleanup();
				success = true;
			} finally {
				this.closeClientDrv(success);
				this.ftpLogout();
				this.addChronology(success);
			}
		} finally {
			this.endLogProcessing(success);
		}
	}

	/**
	 * Logs the final status of the transaction
	 * 
	 * @param success
	 */
	protected void endLogProcessing(boolean success) {
		String methodName = "endProcessing";
		String logMessage = "  !!!! FAILED !!!";
		if (success) {
			logMessage = "  !!!! SUCCESS !!!";
		}
		ASCLogger.info(this.getClass(), methodName, new StringBuffer().append(
				logMessage).append(" for task:").append(task), true);
	}

	/**
	 * Initializes request definition
	 */
	protected void init() {
		request = requestDAO.getByRequestID(task.getRequestId());
		clientDrvCmd = new ClientDrvCommand(this.getClientDrvCommand(), this
				.getGreetingWaitTime());
		ftp = new FTPCommand();
		this.timeout = ApplicationInfo.getInstance().getIntegerPropertyValue(
				PropertyConstants.IPSWEEP_TIMEOUT, "120000");
		this.setUpEnvironment();
	}

	/**
	 * Adds chronoogy entry. This is need for customer connectivity information
	 * 
	 * @param success
	 */
	protected void addChronology(boolean success) {
		Map<String, String> chronologyMap = new HashMap<String, String>();
		if (!ASCUtil.isNullOrEmptyObject(this.sweepProperties)
				&& !ASCUtil.isNullOrEmpty(this.sweepProperties
						.get(ISweepPropertiesDAO.SCRIPT_NAME))) {
			chronologyMap.put(GSCCUtil.APPLICATION, this.sweepProperties
					.get(ISweepPropertiesDAO.SCRIPT_NAME));
		} else {
			chronologyMap.put(GSCCUtil.APPLICATION, "AutoSC");
		}
		chronologyMap.put(GSCCUtil.ATTEMPT_NUM, (task.getFailedCount() + 1)
				+ "");
		chronologyMap.put(GSCCUtil.COMMENT, this.createChronologyComment()
				.toString());
		String completionCode = "Failure";
		if (success) {
			completionCode = "Success";
		}
		chronologyMap.put(GSCCUtil.COMPLETION_CODE, completionCode);
		chronologyMap.put(GSCCUtil.LOGIN_ID, "asc_acct");
		chronologyMap.put(GSCCUtil.PLATFORM, "AutoSc - IPSWEEP");
		chronologyMap.put(GSCCUtil.SYSTEM_ID, request.getSystemId());
		chronologyMap.put(GSCCUtil.TYPE_ID, "Outbound");
		GSCCUtil.addChronology(chronologyMap);
	}

	/**
	 * Creates the chronology comment to be inserted into Chronology table
	 * 
	 * @param client
	 *            - IPSWEEP Client
	 * @return - String Buffer containing the modified comment
	 */
	private StringBuffer createChronologyComment() {
		StringBuffer comment = new StringBuffer();
		// We need to get requester details just for logging perse
		Long requestListID = request.getRequestListId();
		List<ActRequestListUser> requestListUser = requestUserDAO
				.getByRequestListID(requestListID);
		comment.append(" ").append("RequestListID:").append(requestListID);
		if (!ASCUtil.isNullOrEmptyObject(requestListUser)) {
			String sentFrom = ASCUtil.getSentFromUserInList(requestListUser);
			if (!ASCUtil.isNullOrEmpty(sentFrom)) {
				comment.append(" ").append("Sent From:").append(sentFrom);
			}
			String replyToAddresses = ASCUtil
					.getReplyToAddresses(requestListUser);
			if (!ASCUtil.isNullOrEmpty(replyToAddresses)) {
				comment.append(" ").append("Reply To:")
						.append(replyToAddresses);
			}
			Integer sendersSSO = ASCUtil.getSendersSSO(requestListUser);
			if (!ASCUtil.isNullOrEmptyObject(sendersSSO)) {
				comment.append(" ").append("SSO:").append(sendersSSO);
			}
		}
		if (!ASCUtil.isNullOrEmptyList(this.retrievedFiles)) {
			comment.append(" ").append("Retrieved Files:");
			for (int iCount = 0; iCount < this.retrievedFiles.size(); iCount++) {
				if (iCount != 0) {
					comment.append(",");
				}
				comment.append(this.retrievedFiles.get(iCount));
			}
		}
		return comment;
	}

	/**
	 * Deletes the files that were transferred to the remote device
	 */
	public void cleanup() {
		String remoteScriptPath = this.sweepProperties
				.get(ISweepPropertiesDAO.REMOTE_SCRIPT);
		this.deleteFile(remoteScriptPath, false);

		for (int iCount = 0; iCount < this.putSweepFiles.size(); iCount++) {
			CfgSweepFileInfo sweepFileInfo = this.putSweepFiles.get(iCount);
			this.deleteFile(sweepFileInfo.getRemoteFileName(), true);
		}

		for (int iCount = 0; iCount < this.scriptOutFiles.length; iCount++) {
			String scriptOutFile = (String) this.scriptOutFiles[iCount];
			this.deleteFile(scriptOutFile, true);
		}
	}

	/**
	 * Deletes a file on remote device
	 * 
	 * @param filePath
	 */
	private void deleteFile(String filePath, boolean suppress) {
		String methodName = "deleteFile";
		try {
			ftp.deleteFile(filePath, suppress);
		} catch (FTPCommandException e) {
			ASCLogger.error(this.getClass(), methodName,
					"Failed to delete remote file:" + filePath);
		}
	}

	/**
	 * Sets up environment for all information needed by the task
	 */
	protected void setUpEnvironment() {
		if (!ASCUtil.isNullOrEmptyObject(request.getRequestDefnId())) {
			// This has been done from retry perspective if we retry we don't
			// want to see commits from last try
			fileTransferDAO.deleteByRequestId(request.getRequestId());
			sweepProperties = requestDefn2SweepPropertiesDAO
					.getSweepPropForRequestDefn(request.getRequestDefnId());
			List<CfgSweepFileInfo> sweepFileInfoLst = sweepFileInfoDAO
					.getSweepFileInfoForRequestDefn(request.getRequestDefnId());
			this.buildPutGetList(sweepFileInfoLst);
			if (!ASCUtil.isNullOrEmptyMap(this.sweepProperties)) {
				this.setUpSweepDir();
			}
			this.jobDir = ASCUtil.getJobDir(request.getRequestId());
			if (!new File(this.jobDir).exists()) {
				if (!new File(this.jobDir).mkdirs()) {
					throw new IPSweepException(new StringBuffer().append(
							"Sweep Job Dir:").append(this.jobDir).append(
							" couldn't be created for request id:"
									+ request.getRequestId()).append(
							" having request definition:").append(
							request.getRequestDefnId()).toString(),
							PRC_SWP_F_MIN_001);
				}
			}
		}
		this.requestProperties = requestPropertiesDAO
				.getRequestPropertiesByRequestId(request.getRequestId());
	}

	/**
	 * Validates that minimum input is present before actually going forward
	 * with task execution
	 */
	private void validateMinimumInput() {
		String methodName = "validateMinimumInput";
		StringBuffer errorMessage = new StringBuffer();
		if (ASCUtil.isNullOrEmptyObject(request.getRequestDefnId())) {
			errorMessage.append(
					"No Request Definition found for request id:"
							+ request.getRequestId()).append("\n");
		} else if (ASCUtil.isNullOrEmptyMap(this.sweepProperties)) {
			errorMessage.append(
					"No sweep properties found for request id:"
							+ request.getRequestId()).append(
					" having request definition:").append(
					request.getRequestDefnId()).append("\n");
		} else if (ASCUtil.isNullOrEmpty(this.sweepDir)) {
			errorMessage.append(
					"Sweep Dir couldn't be computed for request id:"
							+ request.getRequestId()).append(
					" having request definition:").append(
					request.getRequestDefnId()).append("\n");
		} else {
			errorMessage = this.validateSweepProperties();
		}
		if (!ASCUtil.isNullOrEmptyStringBuffer(errorMessage)) {
			throw new IPSweepException(errorMessage.toString(),
					PRC_SWP_F_MIN_001);
		}
		this.validateSweepScriptFilesExist();
		if (ASCUtil.isNullOrEmptyList(this.getSweepFiles)) {
			ASCLogger.warn(this.getClass(), methodName, new StringBuffer()
					.append(
							"No get files found for request id:"
									+ request.getRequestId()).append(
							" having request definition:").append(
							request.getRequestDefnId()).append("\n"));
		}
		if (ASCUtil.isNullOrEmptyList(this.putSweepFiles)) {
			ASCLogger.warn(this.getClass(), methodName, new StringBuffer()
					.append(
							"No put files found for request id:"
									+ request.getRequestId()).append(
							" having request definition:").append(
							request.getRequestDefnId()).append("\n"));
		}
	}

	/**
	 * Validates if required sweep properties have been specified
	 * 
	 * @return
	 */
	private StringBuffer validateSweepProperties() {
		String fieldName = null;
		if (ASCUtil.isNullOrEmpty(this.sweepProperties
				.get(ISweepPropertiesDAO.REMOTE_SCRIPT))) {
			fieldName = "Remote Script Name";
		} else if (ASCUtil.isNullOrEmpty(this.sweepProperties
				.get(ISweepPropertiesDAO.EXECUTION_TIME))) {
			fieldName = "Execution Time";
		} else if (ASCUtil.isNullOrEmpty(this.sweepProperties
				.get(ISweepPropertiesDAO.SCRIPT_NAME))) {
			fieldName = "Script Name";
		} else if (ASCUtil.isNullOrEmpty(this.sweepProperties
				.get(ISweepPropertiesDAO.SWEEP_DIR))) {
			fieldName = "Sweep Script Directory";
		}
		if (!ASCUtil.isNullOrEmpty(fieldName)) {
			return new StringBuffer()
					.append(fieldName)
					.append(
							" not defined in sweep properties for request definiton id:")
					.append(request.getRequestDefnId());
		}
		return null;
	}

	/**
	 * Validates if all the specified sweep script files actually exist or not
	 */
	private void validateSweepScriptFilesExist() {
		StringBuffer missingFiles = new StringBuffer();
		if (!new File(this.sweepDir).exists()) {
			missingFiles.append("\n").append(
					"Sweep Directory doesn't exists, Sweep Dir:").append(
					this.sweepDir);
		} else {
			File sweepFile = new File(this.sweepDir, this.sweepProperties
					.get(ISweepPropertiesDAO.SCRIPT_NAME));
			if (!sweepFile.exists()) {
				missingFiles.append("\n").append(sweepFile.getAbsolutePath());
			}
		}
		for (int iCount = 0; iCount < putSweepFiles.size(); iCount++) {
			File supportFile = new File(this.sweepDir, putSweepFiles
					.get(iCount).getLocalFileName());
			if (!supportFile.exists()) {
				missingFiles.append("\n").append(supportFile.getAbsolutePath());
			}
		}
		if (!ASCUtil.isNullOrEmptyStringBuffer(missingFiles)) {
			throw new IPSweepException(new StringBuffer(
					"Following sweep script files couldn't be found:").append(
					missingFiles).toString(), PRC_SWP_F_VAL_002);
		}
	}

	/**
	 * Separates put and get files into two separate lists
	 * 
	 * @param sweepFileInfoLst
	 */
	private void buildPutGetList(List<CfgSweepFileInfo> sweepFileInfoLst) {
		String methodName = "buildPutGetList";
		this.putSweepFiles = new ArrayList<CfgSweepFileInfo>();
		this.getSweepFiles = new ArrayList<CfgSweepFileInfo>();
		if (ASCUtil.isNullOrEmptyList(sweepFileInfoLst)) {
			return;
		}
		for (int iCount = 0; iCount < sweepFileInfoLst.size(); iCount++) {
			CfgSweepFileInfo sweepFileInfo = sweepFileInfoLst.get(iCount);
			if (ISweepFileInfoDAO.DOWNLOAD_TO_DEVICE
					.equalsIgnoreCase(sweepFileInfo.getDirection())) {
				this.putSweepFiles.add(sweepFileInfo);
			} else if (ISweepFileInfoDAO.UPLOAD_FROM_DEVICE
					.equalsIgnoreCase(sweepFileInfo.getDirection())) {
				this.getSweepFiles.add(sweepFileInfo);
			} else {
				ASCLogger.error(this.getClass(), methodName,
						"Ignoring sweep file info because of unknown direction specified:"
								+ sweepFileInfo, new IPSweepException(
								"Unknown Direction"));
			}
		}
	}

	/**
	 * Downloads script and support files
	 */
	protected void downloadSetup() {
		this.downloadScript();
		this.downloadSupport();
	}

	/**
	 * Downloads script files
	 */
	private void downloadScript() {
		FileInfo uniquefnInfo = this.getUniqueFileNameFromDevice();
		try {
			this.sweepProperties.put(ISweepPropertiesDAO.REMOTE_SCRIPT,
					uniquefnInfo.getStdoutPath());
			File sweepFile = new File(this.sweepDir, this.sweepProperties
					.get(ISweepPropertiesDAO.SCRIPT_NAME));
			this.putBinaryFile(sweepFile.getAbsolutePath(), uniquefnInfo
					.getStdoutPath(), IPSWEEP_PREFIX, request.getRequestId());
			this.executeChmodCommand(RWX_RW_RW_PERMISSIONS, uniquefnInfo
					.getStdoutPath());
		} catch (FTPCommandException ftpException) {
			throw new IPSweepException("Download script failed with"
					+ ftpException.getMessage(), ftpException,
					PRC_SWP_F_DSC_001);
		}
	}

	/**
	 * Sets up sweep dir
	 */
	private void setUpSweepDir() {
		this.sweepDir = new StringBuffer().append(
				ApplicationInfo.getInstance().getProperty(
						PropertyConstants.ASC_MOUNT_POINT)).append(
				ApplicationInfo.getInstance().getProperty(
						PropertyConstants.ASC_DIR_SEPERATOR)).append(
				ApplicationInfo.getInstance().getProperty(
						PropertyConstants.SWEEP_SCRIPT_DIR_LOCATION)).append(
				ApplicationInfo.getInstance().getProperty(
						PropertyConstants.ASC_DIR_SEPERATOR)).append(
				request.getModality().toLowerCase()).append(
				ApplicationInfo.getInstance().getProperty(
						PropertyConstants.ASC_DIR_SEPERATOR)).append(
				this.sweepProperties.get(ISweepPropertiesDAO.SWEEP_DIR))
				.toString();
	}

	/**
	 * Runs uniquefn command to get unique file name
	 * 
	 * @return
	 */
	private FileInfo getUniqueFileNameFromDevice() {
		FileInfo uniquefnInfo = null;
		try {
			String uniquefn = "uniquefn";
			String uniquefnResponse = clientDrvCmd.executeUniquefn();
			uniquefnInfo = this.parseFileInfoFromResponse(uniquefnResponse,
					uniquefn);
		} catch (ClientDrvCommandException rexecCommandException) {
			throw new IPSweepException(
					"Download script failed as unique file information couldn't be obtained"
							+ rexecCommandException.getMessage(),
					rexecCommandException, PRC_SWP_F_UFN_001);
		}
		return uniquefnInfo;
	}

	/**
	 * Download the support files
	 */
	private void downloadSupport() {
		if (ASCUtil.isNullOrEmptyList(putSweepFiles)) {
			return;
		}

		try {
			for (int iCount = 0; iCount < putSweepFiles.size(); iCount++) {
				CfgSweepFileInfo putFileInfo = putSweepFiles.get(iCount);
				this.putBinaryFile(new File(this.sweepDir, putFileInfo
						.getLocalFileName()).getAbsolutePath(), putFileInfo
						.getRemoteFileName(), IPSWEEP_PREFIX, request
						.getRequestId());
				this.executeChmodCommand(RWX_RW_RW_PERMISSIONS, putFileInfo
						.getRemoteFileName());
			}
		} catch (FTPCommandException ftpException) {
			throw new IPSweepException("Download support files failed with"
					+ ftpException.getMessage(), ftpException,
					PRC_SWP_F_DSC_002);
		}
	}

	/**
	 * Executes the command
	 */
	protected void executeScript() {
		String methodName = "executeScript";
		int scriptExecutionTime = Integer.parseInt(this.sweepProperties.get(
				ISweepPropertiesDAO.EXECUTION_TIME).trim());
		// Setting the timeout is specified so as to know how long to wait
		// before quitting
		this.setRexecTimeout(scriptExecutionTime * 1000);
		FileInfo scriptResultFI = null;
		ClientDrvHeartBeat hb = new ClientDrvHeartBeat(clientDrvCmd,
				IPSWEEP_PREFIX + Thread.currentThread().getName(), true);
		String cmd = null;
		try {
			if (hb.isBeatable()) {
				hb.start();
			}
			cmd = this.getScriptCommand();
			String executeCmdReply = clientDrvCmd.executeSweepCommand(cmd);
			ASCLogger.info(this.getClass(), methodName, new StringBuffer()
					.append("Cmd:").append(cmd).append(" Reply:").append(
							executeCmdReply), true);
			scriptResultFI = this.parseFileInfoFromResponse(executeCmdReply,
					cmd);
		} catch (ClientDrvCommandException rexecCmdException) {
			throw new IPSweepException(
					"Failed to execute command sweep " + cmd,
					rexecCmdException, PRC_SWP_F_XSC_001);
		} finally {
			hb.done();
			// Resetting the timeout
			this.setRexecTimeout(timeout);
		}
		this.retrieveFiles(scriptResultFI);
	}

	/**
	 * Retrieves Stdout and StdErr files
	 * 
	 * @param commandResultFile
	 */
	private void retrieveFiles(FileInfo commandResultFile) {
		// fetch stdout and stderr for sweep script from target system
		scriptOutFile = new File(jobDir, "scriptStd.txt");
		scriptErrFile = new File(jobDir, "scriptErr.txt");
		if (commandResultFile.getStdoutSize() > 0) {
			getBinaryFile(scriptOutFile, commandResultFile.getStdoutPath(),
					commandResultFile.getStdoutSize(), IPSWEEP_PREFIX, request
							.getRequestId(), true);
		}
		if (commandResultFile.getStderrSize() > 0) {
			getBinaryFile(scriptErrFile, commandResultFile.getStderrPath(),
					commandResultFile.getStderrSize(), IPSWEEP_PREFIX, request
							.getRequestId(), true);
		}

	}

	/**
	 * Parses file information retruned from clientdrv command
	 * 
	 * @param response
	 * @param cmd
	 * @return
	 */
	private FileInfo parseFileInfoFromResponse(String response, String cmd) {
		String methodName = "parseFileInfoFromResponse";
		FileInfo parsedResponse = IIPResponseParser
				.parseFileInformation(response);
		ASCLogger.info(this.getClass(), methodName, new StringBuffer().append(
				"Clientdrv:").append(cmd).append(" response:").append(response)
				.append(" is parsed to :").append(parsedResponse));
		if (ASCUtil.isNullOrEmptyObject(parsedResponse)) {
			throw new IPSweepException(new StringBuffer().append("Clientdrv:")
					.append(cmd).append(" returned null response").toString(),
					PRC_SWP_F_CDR_001);
		}
		if (!parsedResponse.isValid()) {
			throw new IPSweepException(new StringBuffer().append("Clientdrv:")
					.append(cmd).append(" response:").append(response).append(
							" is not in valid format").toString(),
					PRC_SWP_F_CDR_002);
		}
		return parsedResponse;
	}

	/**
	 * Generates parameters for script command
	 * 
	 * @return
	 */
	private String getScriptCommand() {
		String subXArgsValues = this.subXArgsValues(this.sweepProperties
				.get(ISweepPropertiesDAO.ARGUMENTS));
		StringBuffer command = new StringBuffer().append(this.sweepProperties
				.get(ISweepPropertiesDAO.REMOTE_SCRIPT));
		if (!ASCUtil.isNullOrEmpty(subXArgsValues)) {
			command.append(" ").append(subXArgsValues);
		}
		return command.toString();
	}

	/**
	 * Specified whether the sweep has dynamic arguments or not
	 * 
	 * @return
	 */
	private boolean hasDynamicArguments() {
		if (!ASCUtil.isNullOrEmpty(this.sweepProperties
				.get(ISweepPropertiesDAO.IS_DYNAMIC))
				&& "true".equalsIgnoreCase(this.sweepProperties
						.get(ISweepPropertiesDAO.IS_DYNAMIC))) {
			return true;
		}
		return false;
	}

	/**
	 * Substitute the axrgs valus into the sweep.prm.tmpl file configuration
	 * 
	 * @param line
	 * @return the corrected line
	 */
	private String subXArgsValues(String line) {
		if (!this.hasDynamicArguments()) {
			return line;
		}
		if (ASCUtil.isNullOrEmptyObject(requestProperties)) {
			return line;
		}
		String methodName = "subXArgsValues";
		RE checkForVar;
		try {
			checkForVar = new RE("%\\w+%");
			if (checkForVar.match(line)) {
				for (Iterator<String> iter = requestProperties.keySet()
						.iterator(); iter.hasNext();) {
					String name = (String) iter.next();
					RE nameSub = new RE("^(.*)%" + name + "%(.*)$");
					if (nameSub.match(line)) {
						ASCLogger.info(this.getClass(), methodName,
								"Dynamic Substitute: " + name + " value: "
										+ requestProperties.get(name), true);
						line = nameSub.getParen(1)
								+ (String) requestProperties.get(name)
								+ nameSub.getParen(2);
					}
				}
			}
		} catch (RESyntaxException reException) {
			throw new IPSweepException(
					"Failed performing RE Conversion using requestProperties:"
							+ requestProperties, reException, PRC_SWP_F_DYN_001);
		}
		return line;
	}

	/**
	 * Sets timeout for rexec command
	 * 
	 * @param timeoutMillis
	 */
	private void setRexecTimeout(int timeoutMillis) {
		try {
			ASCLogger.info(this.getClass(), "setRexecTimeout",
					new StringBuffer("Setting rexec timeout to:")
							.append(timeoutMillis));
			clientDrvCmd.setTimeout(timeoutMillis);
		} catch (ClientDrvCommandException rexecCommandException) {
			throw new IPSweepException(rexecCommandException, PRC_SWP_F_RXT_001);
		}
	}

	/**
	 * Fetches results files and returned by script out file and those
	 * configured in get files parameter
	 */
	public void fetchResults() {
		String methodName = "fetchResults";
		scriptOutFiles = this.getScriptOutFiles();
		int extraCount = 1;
		int iCount = 0;
		for (; iCount < scriptOutFiles.length; iCount++) {
			String remoteFile = (String) scriptOutFiles[iCount];
			File localFile = null;
			String type = null;
			if (iCount < this.getSweepFiles.size()) {
				CfgSweepFileInfo getFile = this.getSweepFiles.get(iCount);
				localFile = new File(jobDir, getFile.getLocalFileName());
				type = getFile.getFileType();
			} else {
				ASCLogger.info(this.getClass(), methodName, new StringBuffer()
						.append("Filesback: ").append(remoteFile).append(
								" with no 'filesback' parameter line"));
				try {
					localFile = File.createTempFile("file", "back", new File(
							jobDir));
				} catch (IOException ioException) {
					throw new IPSweepException(
							"Failed creating filesback filename:" + localFile,
							ioException, PRC_SWP_F_EXF_001);
				}
				type = "extra." + extraCount;
				extraCount++;

			}
			retrievedFiles.add(new File(remoteFile).getName());
			this.getBinaryFile(localFile, remoteFile, 0, IPSWEEP_PREFIX,
					request.getRequestId());
			if (type == null) {
				type = "extra." + extraCount;
				extraCount++;
			}
		}
		for (; iCount < this.getSweepFiles.size(); iCount++) {
			ASCLogger.info(this.getClass(), methodName, "Filesback parameter: "
					+ this.getSweepFiles.get(iCount) + " with no Filesback");
		}
	}

	/**
	 * Reads contents of script out files
	 * 
	 * @return
	 */
	private Object[] getScriptOutFiles() {
		try {
			return ASCFileHandler.readNonEmptyLines(this.scriptOutFile
					.getAbsolutePath());
		} catch (IOException ioException) {
			throw new IPSweepException("Couldn't read script out contents for:"
					+ this.scriptOutFile.getAbsolutePath(), ioException,
					PRC_SWP_F_RSO_001);
		}
	}

	/**
	 * Performs ftp logout
	 */
	protected void ftpLogout() {
		if (!ASCUtil.isNullOrEmptyObject(ftp)) {
			ftp.logout();
		}
	}

	/**
	 * Brinsg remote device to 3. domain
	 */
	protected void makeConnection() {
		try {
			ConnectivityUtil.makeConnection(request.getSystemId(), this
					.getClass().getName(), 1, null);
		} catch (MakeConnectionException mcException) {
			String message = "Make Connection call failed for systemId:"
					+ request.getSystemId();
			throw new IPSweepException(message, mcException, PRC_SWP_F_MCF_001);
		}
	}

	/**
	 * Performs login, first attempt using preferred user then by rest
	 */
	protected void performLogin() {
		Map<String, String> loginDetails = loginDetailsDAO
				.getLoginDetailsByRequestID(task.getRequestId());
		if (ASCUtil.isNullOrEmptyMap(loginDetails)) {
			throw new IPSweepException(
					"No login details specified for request id:"
							+ request.getRequestId(), PRC_SWP_F_VAL_001);
		}
		this.peformPriorityLogin(loginDetails);
	}

	/**
	 * Time to connect using rexec and run clientDrv
	 */
	protected void runClientDrv() {
		String methodName = "runClientDrv";
		try {
			ASCLogger.info(this.getClass(), methodName, new StringBuffer(
					"Performing rexec as:").append(acceptedUserName));
			clientDrvCmd.connect(request.getIpAddress(), this.acceptedUserName,
					this.acceptedPassword, timeout);
		} catch (ClientDrvCommandException rexecCommandException) {
			throw new IPSweepException(
					"IPSweep failed as rexec was unsuccessful",
					rexecCommandException, PRC_SWP_F_CDR_003);
		}
		ASCLogger.info(this.getClass(), methodName, new StringBuffer(
				"Rexec successful for host:").append(request.getIpAddress()));
	}

	/**
	 * Try and perform priority login, if possible if not try normal login
	 * 
	 * @param loginDetails
	 */
	private void peformPriorityLogin(Map<String, String> loginDetails) {
		String methodName = "peformPriorityLogin";
		String priorityUser = this.getPriorityUser();
		if (!ASCUtil.isNullOrEmpty(loginDetails.get(priorityUser))) {
			try {
				ftp.login(request.getIpAddress(), priorityUser, loginDetails
						.get(priorityUser));
				this.acceptedUserName = priorityUser;
				this.acceptedPassword = loginDetails.get(priorityUser);
			} catch (FTPCommandException e) {
				ASCLogger.error(this.getClass(), methodName, this
						.getFailedLoginMessage(priorityUser, loginDetails
								.get(priorityUser)));
				this.performNormalLogin(loginDetails, priorityUser);
			} catch (InvalidLoginCredentialsException e) {
				ASCLogger.error(this.getClass(), methodName, this
						.getFailedLoginMessage(priorityUser, loginDetails
								.get(priorityUser)));
				this.performNormalLogin(loginDetails, priorityUser);
			}
		} else {
			this.performNormalLogin(loginDetails, priorityUser);
		}
	}

	/**
	 * Returns the priority user
	 * 
	 * @return
	 */
	private String getPriorityUser() {
		String priorityUser = null;
		priorityUser = modelTypePropDAO.getValueForProperty(request
				.getModality(), request.getProductLine(), request
				.getModelType(), PropertyConstants.PRIORITY_USER_NAME);
		if (ASCUtil.isNullOrEmpty(priorityUser)) {
			priorityUser = ApplicationInfo.getInstance().getProperty(
					PropertyConstants.PRIORITY_USER_NAME, ROOT);
		}
		return priorityUser;
	}

	/**
	 * Gets default client drv command
	 * 
	 * @return
	 */
	private String getClientDrvCommand() {
		String clientDrvCmd = null;
		clientDrvCmd = modelTypePropDAO.getValueForProperty(request
				.getModality(), request.getProductLine(), request
				.getModelType(), PropertyConstants.CLIENT_DRV_COMMAND);
		if (ASCUtil.isNullOrEmpty(clientDrvCmd)) {
			clientDrvCmd = ApplicationInfo.getInstance().getProperty(
					PropertyConstants.CLIENT_DRV_COMMAND,
					"~insite/bin/clientdrv");
		}
		return clientDrvCmd;
	}

	/**
	 * Gets default greeting wait time
	 * 
	 * @return
	 */
	private int getGreetingWaitTime() {
		String greetingWaitTime = null;
		int waitTime = -1;
		greetingWaitTime = modelTypePropDAO.getValueForProperty(request
				.getModality(), request.getProductLine(), request
				.getModelType(), PropertyConstants.CLIENT_DRV_GREETING_WAIT);
		if (ASCUtil.isNullOrEmpty(greetingWaitTime)) {
			waitTime = ApplicationInfo.getInstance().getIntegerPropertyValue(
					PropertyConstants.CLIENT_DRV_GREETING_WAIT, "3");
		} else {
			waitTime = Integer.parseInt(greetingWaitTime.trim());
		}
		return waitTime;
	}

	/**
	 * Try and perform normal login
	 * 
	 * @param loginDetails
	 * @param priorityUser
	 */
	private void performNormalLogin(Map<String, String> loginDetails,
			String priorityUser) {
		String methodName = "performNormalLogin";
		Iterator<String> loginNames = loginDetails.keySet().iterator();
		boolean loggedIn = false;
		while (loginNames.hasNext()) {
			String loginName = loginNames.next();
			if (!priorityUser.equals(loginName)) {
				try {
					ftp.login(request.getIpAddress(), loginName, loginDetails
							.get(loginName));
					loggedIn = true;
					this.acceptedUserName = loginName;
					this.acceptedPassword = loginDetails.get(loginName);
					break;
				} catch (FTPCommandException e) {
					ASCLogger.error(this.getClass(), methodName, this
							.getFailedLoginMessage(loginName, loginDetails
									.get(loginName)));
				} catch (InvalidLoginCredentialsException e) {
					ASCLogger.error(this.getClass(), methodName, this
							.getFailedLoginMessage(loginName, loginDetails
									.get(loginName)));
				}
			}
		}
		if (!loggedIn) {
			// I am using keyset as i don't want to expose passcodes
			StepLogger.addStepLog(new StringBuffer(
					"Failed to login with all any available logins:").append(
					loginDetails.keySet()).append(" for Request:").append(
					request));
			throw new IPSweepException(
					"Cannot login even after trying all available logins, loginDetails:"
							+ loginDetails.keySet(), PRC_SWP_F_FTP_001);
		}
	}

	/**
	 * Message to log if login fails
	 * 
	 * @param login
	 * @param passcode
	 * @return
	 */
	private String getFailedLoginMessage(String login, String passcode) {
		return new StringBuffer().append("Login failed for ip address:")
				.append(request.getIpAddress()).append(" Request Id:").append(
						request.getRequestId()).append(" using login name:")
				.append(login).append(" and passcode:").append(passcode)
				.toString();
	}

	/**
	 * Closes Client drv - command
	 * 
	 * @param success
	 */
	private void closeClientDrv(boolean success) {
		if (ASCUtil.isNullOrEmptyObject(clientDrvCmd)) {
			return;
		}
		String methodName = "closeClientDrv";
		try {
			if (success) {
				clientDrvCmd.completeTransWithSuccessMsg();
			} else {
				clientDrvCmd.completeTransWithFailureMsg();
			}
		} catch (ClientDrvCommandException e) {
			ASCLogger.error(this.getClass(), methodName,
					"Failed to close rexec for client drv transaction status:"
							+ success);
		}
	}

	/**
	 * @param loginDetailsDAO
	 *            the loginDetailsDAO to set
	 */
	public void setLoginDetailsDAO(IRequestLoginDetailsDAO loginDetailsDAO) {
		IPSweepTask.loginDetailsDAO = loginDetailsDAO;
	}

	/**
	 * @param requestDAO
	 *            the requestDAO to set
	 */
	public void setRequestDAO(IRequestDAO requestDAO) {
		IPSweepTask.requestDAO = requestDAO;
	}

	/**
	 * @param sweepFileInfoDAO
	 *            the sweepFileInfoDAO to set
	 */
	public void setSweepFileInfoDAO(ISweepFileInfoDAO sweepFileInfoDAO) {
		IPSweepTask.sweepFileInfoDAO = sweepFileInfoDAO;
	}

	/**
	 * @param requestUserDAO
	 *            the requestUserDAO to set
	 */
	public void setRequestUserDAO(IRequestListUserDAO requestUserDAO) {
		IPSweepTask.requestUserDAO = requestUserDAO;
	}

	/**
	 * @param requestPropertiesDAO
	 *            the requestPropertiesDAO to set
	 */
	public void setRequestPropertiesDAO(
			IRequestPropertiesDAO requestPropertiesDAO) {
		IPSweepTask.requestPropertiesDAO = requestPropertiesDAO;
	}

	/**
	 * @param modelTypePropDAO
	 *            the modelTypePropDAO to set
	 */
	public void setModelTypePropDAO(IModelTypePropDAO modelTypePropDAO) {
		IPSweepTask.modelTypePropDAO = modelTypePropDAO;
	}

	/**
	 * 
	 * @param requestDefn2SweepPropertiesDAO
	 */
	public void setRequestDefn2SweepPropertiesDAO(
			IRequestDefn2SweepPropertiesDAO requestDefn2SweepPropertiesDAO) {
		IPSweepTask.requestDefn2SweepPropertiesDAO = requestDefn2SweepPropertiesDAO;
	}

	/**
	 * 
	 * @param requestTypeDAO
	 */
	public void setRequestTypeDAO(IRequestTypeDAO requestTypeDAO) {
		IPSweepTask.requestTypeDAO = requestTypeDAO;
	}

	/**
	 * @return the requestTypeDAO
	 */
	public IRequestTypeDAO getRequestTypeDAO() {
		return requestTypeDAO;
	}
}
